package com.tengtong.duang.commons.utils.helper;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.tengtong.duang.commons.utils.ClassUtils;

/**
 * 使用代理来实现“视图”概念，也就是说，不管对象是什么类型，我们都可以看作某种类型加以使用。 
 * 
 * 当然，前提就是这些对象都有一个相同的方法。
 * 
 * @author Jai
 * @since: v1.0
 */
final public class ViewBoundHelper {

	private ViewBoundHelper() {
	};

	/**
	 * 这是一个代理类，代理某个接口
	 *
	 *
	 * @author jai
	 * @since: v1.0
	 */
	private static final class Handler implements InvocationHandler {
		private final Object target;
		private final Class<?> targetClass;

		public Handler(Object t) {
			this.target = t;
			this.targetClass = target.getClass();
		}
		
		public Handler(Class<?> targetClass){
			this.target = null;
			this.targetClass = targetClass;
		}

		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			return getDeclaredMethod(method).invoke(target, args);
		}

		Method getDeclaredMethod(Method m) {
			Class<?> cls = targetClass;
			while (true) {
				try {
					m = cls.getDeclaredMethod(m.getName(), m.getParameterTypes());
					break;
				}
				catch (NoSuchMethodException ex) {
					cls = cls.getSuperclass();
					if (cls == null) {
						return null;
					}
				}
			}
			return m;
		}
	}

	public static Object asIs(Object o, Class<?> intf) {
		Handler handler = new Handler(o);
		for (Method method : intf.getDeclaredMethods()) {
			if (handler.getDeclaredMethod(method) == null) {
				throw new ClassCastException(o.getClass() + "can't be viewed as " + intf.getName());
			}
		}
		return Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class[] { intf }, handler);
	}

	public static Object asIs(Class<?> source, Class<?> intf) {
		Handler handler = new Handler(source);
		for (Method method : intf.getDeclaredMethods()) {
			if (handler.getDeclaredMethod(method) == null) {
				throw new ClassCastException(source + "can't be viewed as " + intf.getName());
			}
		}
		return Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class[] { intf }, handler);
	}
}